#include <thread>
#include <functional>
#include <mutex>
#include <memory>
#include <iostream>
#include "serialio.hpp"
#include "serial.hpp"
#include "sys_var.hpp"
#include "misc.h"
#include "at.hpp"
#if defined( __USE_MEMPOOL__ )
    #include <jemalloc/jemalloc.h>
#endif // defined

serialIO::serialIO(const std::string& name, io_type type , filterList * l )
:filterIO( type , l ), is_timer_call( false ), ptSerial( nullptr )
{
	// 读取配置文件
	p_buff          = nullptr;
	p_packed_buff   = nullptr;
	m_len           = 0;
	m_pack_len      = 0;
	m_is_caculate   = false;
	m_error         = OK;
	
	std::shared_ptr<CSysVar>  ptSysVar = GetOrCreateSysVar();
	std::string   str = "/serial/" + name + "/";
	m_name = name;
	serial_param   param;
	if( ptSysVar ){
		init_sys_var( str , ptSysVar , param );
	}else{
		throw ERR_GET_SYSVAR;
	}
	// 启动串口
	try{
		init_serial_port( param );
		//__show_line_2();
	}catch( serial::err_code ){
		// TODO , 响应串口初始化的异常操作
	}catch( std::bad_alloc& e ){
		__show_line_1( "%s%s%s" , TRED , "Initialize serial port fail for allocate memory fail." , TNORMAL );
		abort();
	}catch( ... ){
		__show_line_1( "%s%s%s" , TRED , "Unknow error when initialize serial port" , TNORMAL );
	}
}

serialIO::~serialIO()
{
    	if( p_buff != nullptr ) free( p_buff );
}

void serialIO :: init_sys_var( const std::string& str , std::shared_ptr< CSysVar >& ptvar , serial_param& param )
{
	int baud = 0 , clen = 8, stop  = 0 , parity  = 0, flow = 0 , pkg = 0 , pkg_l=0 , pkg_itval = 0;
	std::string real_name;
	
	ptvar->GetValue( str + "baud"       , baud   );
	ptvar->GetValue( str + "stop"       , stop   );
	ptvar->GetValue( str + "parity"     , parity );
	
	ptvar->GetValue( str + "flow"       , flow   );
	ptvar->GetValue( str + "packet"     , pkg    );
	ptvar->GetValue( str + "pkg_length" , pkg_l );
	ptvar->GetValue( str + "pkg_intval" , pkg_itval );
	ptvar->GetValue( str + "file"       , real_name );
	ptvar->GetValue( str + "char_len"   , clen );
    
	param.real_name = std::move( real_name );
	param.baud      = baud;
	param.clen      = clen;
	param.stop      = stop;
	param.flow      = flow;
	param.parity    = parity;

	m_pkg_mtd = (pack_mtd::value)pkg;
	m_pack_len = pkg_l;
	m_pkg_intval = pkg_itval;
}

void serialIO :: init_pkg_timer()
{
	// 启动定时打包程序
	if( m_pkg_mtd == pack_mtd::value::TIME || m_pkg_mtd == pack_mtd::value::MIX ){
		m_pkg_timer.SetOwner([this]{
			// 通知按照定时发送数据
			is_timer_call = true;

			for( size_t i = 0; i < ptList.size(); i ++ )
				forward( ptList[ i ]->begin() , m_len , p_buff , OK );

			m_pkg_timer.Start( -1, true );

		});
		m_pkg_timer.Start( m_pkg_intval , true );
	}
}

void serialIO :: init_serial_port( const serial_param& param )
{
	std::unique_ptr< serial > tmp(  new serial( param.real_name ) );
	ptSerial = std::move( tmp );
	if( ptSerial ){
		ptSerial->SetBufferSize( m_pack_len.load() );               // 指定缓冲区大小

		ptSerial->evtOpen([this](serial::err_code){ 
			__show_line_1( "%s serial port is opened%s", TYELLOW , TNORMAL );
			// 启动串口通讯模块 
			ptSerial->run();

			init_pkg_timer();                           // 初始化时间相关的打包计时器
		});
		ptSerial->evtClose([this](serial::err_code){});

		ptSerial->evtRecv( [this](size_t len , const char* data , serial::err_code e1 ){// 设置接收回调函数
			err_code e = OK;
			if( e1 == serial::OK ){
				m_is_caculate = false;
				OnRecv( len , data , e );
			}
		});
        	ptSerial->evtSend( [this](size_t len , serial::err_code){
            		__show_line_1( "%s serial SEND %ld bytes%s", TYELLOW ,  len , TNORMAL );
        	});
        	// 打开串口
        	ptSerial->open( param.baud , param.clen , static_cast<serial::stop::value>( param.stop ) , 
                 	static_cast<serial::parity::value>( param.parity ) ,  
                 	static_cast<serial::flow::value>( param.flow ) );
        
    	}
}

bool serialIO::on_forward( size_t len , const char * data , err_code &err , size_t& rlen , char ** rd )
{
	bool ret = false;
	// 判断打包类型，根据打包类型的不同分别处理

	if( process_at_data( data , len ) ){
		*rd = ( char *)data;
		rlen = len;
		return true;
	}
	switch(  m_pkg_mtd.load() ){
	case pack_mtd::value::NONE: // 不分包
	default:
		*rd = ( char *)data;
		rlen = len;
		ret = true;
		break;
	case pack_mtd::value::LENGTH: // 按长度分包
		ret = do_length_pack( len , data , rlen , rd );
		break;
	case pack_mtd::value::TIME:   // 定时分包
		ret = do_timer_pack( len , data , rlen , rd );
		break;
	case pack_mtd::value::MIX:    // 混合分包
		if( (int32_t)(m_len + len) >= m_pack_len ){// 如果长度达到预定长度则按照预定长度的分包发送
		m_pkg_timer.Stop();
		ret = do_length_pack( len , data , rlen , rd );
		m_pkg_timer.Start( -1 , true );
		}else{   // 如果没有达到长度，则根据时间，超时后后发送；如果没有超时则将
			// 数据添加到缓冲区尾部
		ret = do_timer_pack( len , data , rlen , rd );
		}
		break;
	}

	return ret;
}

bool serialIO :: process_at_data( const char *data , size_t len )
{
	bool ret = false;
	ret = IsInSerialAT();
	
	if( ret == false ){
		ret = ( memcmp( data , "+++" , len ) == 0 );
		
		if( ret == false )
		ret = IsATHandShake();
		
	}
	return ret;
}

bool serialIO::do_length_pack( size_t len , const char * data , size_t &rlen , char ** rd )
{
	/*
	* 按照长度的分包程序。这个程序应该能够，将超过规定长度的过个数据包。分成规定长度的数据包
	* 发送出去
	*/
	bool ret = false;

	if( m_is_caculate == false ){
		if( (int32_t)(m_len + len) >= m_pack_len * 2 ){      // 总体数据包长度超过或者等于2个规定数据包
			ret = pack_leng_db( data , len , rd , rlen );
		}else if( (int32_t)(m_len + len) > m_pack_len.load() ){     // 总体数据包长度超过一个数据包，但是不足两个数据包
			ret = pack_leng_final( data , len , rd , rlen );
		}else{                                    // 总体数据包长度不足一个规定数据包
			pack_leng_sem( data , len );
			ret = false;
		} 
	}else{
		if( p_packed_buff ){
			rlen = m_pack_len;
			char * tmp_buff = ( char *)malloc( m_pack_len.load() );
			if( tmp_buff ){
				memcpy( tmp_buff , p_packed_buff , m_pack_len.load() );
				ret = true;
				*rd = tmp_buff;
			}else{
				*rd = nullptr;
			}
		}
	}

	return ret;
}

void serialIO::pack_leng_sem( const char * data , size_t len )
{
	__show_line_2();
	if( p_buff == nullptr ){
		if( m_pack_len > 0 )
			p_buff = ( char *)malloc( m_pack_len.load() );
		if( p_buff == nullptr ){
			__show_line_1( "%s%s%s" , TRED , "Allocate memory fail." , TNORMAL );
			abort();
		}
	}
	if( p_buff != nullptr ){
		if( m_is_caculate == false ){   // 确认计算标识，避免多个通道引起的重复打包处理
			memcpy( p_buff + m_len , data , len );
			m_len += len;

			m_is_caculate = true;
		}
	}
}

bool serialIO::pack_leng_final( const char * data , size_t len , char **rd , size_t& rlen )
{
	bool ret = false;
	__show_line_2();
	// 发送数据
	if( p_buff == nullptr ){
		p_buff = ( char* )malloc( m_pack_len.load() );
		m_len = 0;
	}
	__show_line_1( "%s m_is_caculate = %d %s", TYELLOW , m_is_caculate.load() , TNORMAL );
	if( p_buff == nullptr ) return false;        
	// 将数据保存到缓存，凑够一包数据
	if( m_is_caculate == false ){
		__show_line_2();
		char * p_tmp_buff = p_buff;
		memcpy( p_tmp_buff + m_len , data , m_pack_len.load() - m_len );
		if( p_packed_buff == nullptr ){
			__show_line_2();
			p_packed_buff = ( char *)malloc( m_pack_len.load() );
		}
		if( p_packed_buff ){
			__show_line_2();
			memcpy( p_packed_buff , p_tmp_buff , m_pack_len.load() );
		}
		// 将剩下的数据保存在缓冲区
		p_buff = ( char * )malloc( m_pack_len.load() );
		if( p_buff ){
			__show_line_2();
			int len1 = len - m_pack_len + m_len;
			memcpy( p_buff , data + m_pack_len - m_len , len1 );
			ret = true;                       // 发送数据
			m_len = len - len1;
			m_error = OK;
			*rd = p_tmp_buff;
			rlen = m_pack_len;

			m_is_caculate = true;
			__show_line_2();
		}else{// 内存分配失败，不发送数据
			__show_line_2();
			ret = false;
			if( p_tmp_buff ){
				p_buff = p_tmp_buff;
			}
			if( p_packed_buff ) free( p_packed_buff );
			*rd = nullptr;
			rlen = len;
			__show_line_2();
		}
	}else{
		__show_line_2();
		char * rst_buff = ( char *)malloc( m_pack_len.load() );
		if( rst_buff ){
			__show_line_2();
			memcpy( rst_buff , p_packed_buff , m_pack_len.load() );
			*rd = rst_buff;
			rlen = m_pack_len;

			ret = true;
		}else{
			__show_line_2();
			*rd = nullptr;
			rlen = 0;

			ret = false;
		}
	}
	__show_line_2();

	return ret;
}

bool serialIO::pack_leng_db( const char * data , size_t len , char **rd , size_t& rlen )
{
	bool ret = false;
	__show_line_2( );
	memcpy( p_buff + m_len , data , m_pack_len - m_len );
	*rd = p_buff;
	rlen = m_pack_len;
	__show_line_2();
	m_len = 0;
	p_buff = ( char * )malloc( m_pack_len );
	size_t tmp_len = len - m_pack_len + m_len;
	char * tmp_data = ( char * )malloc( tmp_len );
	if( tmp_data != nullptr ){
		__show_line_2();
		// 这里稍微延迟一下，等候前面的数据先启动发送任务
		std::thread thd([ this , tmp_data , tmp_len ]{
		std::this_thread::sleep_for( std::chrono::milliseconds( 20 ) );

		for( size_t i = 0; i < ptList.size(); i ++ )
			forward( ptList[ i ]->begin() , tmp_len , tmp_data , OK );
		});
		thd.detach();

		ret = true;
		__show_line_2();
	}else{
		m_error = filterIO::ERR_ALLOC_MEM;
		ret = false;
	}
	__show_line_2();

	return ret;
}

bool serialIO::do_timer_pack( size_t len , const char * data , size_t &rlen , char ** rd )
{
	bool ret = false;
	__show_line_2();
	if( is_timer_call){ // 时间到，执行发送
		__show_line_2();
		m_mutex.lock();
		*rd = p_buff;
		rlen = m_len;

		p_buff = ( char *)malloc( m_pack_len );
		if( p_buff == nullptr ){
			m_error = ERR_ALLOC_MEM;
			ret = false;
		}else{ ret = true;/* ret = true ， 表示允许数据发送到下一个过滤器*/ }
		// m_mutex.unlock();
		is_timer_call = false;
		__show_line_2();
	}
	else{  // 时间没有到，将数据添加到缓冲区后部
		__show_line_2();
	m_mutex.lock();
		p_buff = ( char *)realloc( p_buff , m_len + len );
		if( p_buff != nullptr ){
			memcpy( p_buff + m_len , data , len );
			m_len += len;
		}
		m_mutex.unlock();
		ret = false;
		__show_line_2();
	}

	return ret;
}

bool serialIO::on_backward( size_t len , const char * data , err_code &err , size_t& rlen , char ** rd  )
{
	bool ret = false;
	serial::err_code e;
	if( ptSerial ){
		
		__show_line_1("%s%s%s", TBLUE, "SEND-COM-ON.", TNORMAL);
		__show_line_1("\n%s len1 = %ld , data[1] = %ld, data[2] = %ld %s\n", TRED, len,data[1],data[2], TNORMAL);
		
		e = ptSerial->send( len , data );// 向串口发送出数据

		if( e != serial::OK ){
		// TODO , 错误处理
		}else{

		}
		err = OK;
		*rd = nullptr;
		rlen = 0;
	}
	else{   // TODO , 错误处理
	}
	return ret;
}

void serialIO :: pack( pack_mtd::value v , long t , int32_t l )
{
	if( p_buff && m_pack_len < l ){
		p_buff = ( char *) realloc( p_buff ,  l );
	}else if( p_buff == nullptr ){
		p_buff = ( char *)malloc( l );
	}

	if( p_buff ){
		m_pkg_mtd = v;
		m_pack_len = l;
		m_pkg_intval = t;

		m_pkg_timer( t );
	}else{
		throw ERR_ALLOC_MEM;
	}
}
